package demoMod.anm2editor.ui;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import demoMod.anm2editor.fonts.FontKeys;
import demoMod.anm2editor.localization.LocalizedStrings;
import demoMod.anm2editor.model.KeyFrame;
import demoMod.anm2editor.model.Operation;
import demoMod.anm2editor.model.Project;
import demoMod.anm2editor.model.SpriteSheet;
import demoMod.anm2editor.utils.IdGenerator;
import demoMod.gdxform.abstracts.Container;
import demoMod.gdxform.core.AbstractAction;
import demoMod.gdxform.core.ActionManager;
import demoMod.gdxform.core.FormManager;
import demoMod.gdxform.helpers.FontHelper;
import demoMod.gdxform.interfaces.Element;
import demoMod.gdxform.interfaces.MouseEventSubscriber;
import demoMod.gdxform.ui.GFileSelectWindow;
import demoMod.gdxform.ui.GMenuItem;
import demoMod.gdxform.ui.GMenuList;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class SpriteSheetItem implements Element, MouseEventSubscriber {
    private String id = "";
    private Container<? extends Element> parent;
    private SpriteSheet spriteSheet;
    private final BitmapFont font;
    private boolean mouseDown = false;
    private final RegionManager regionManager = (RegionManager) FormManager.getInstance().getContainerById(RegionManager.ID);
    private final ActionManager actionManager = new ActionManager();

    public SpriteSheetItem(BitmapFont font) {
        this.font = font;
    }

    @Override
    public String getId() {
        return id;
    }

    @Override
    public void setId(String id) {
        this.id = id;
    }

    public SpriteSheet getSpriteSheet() {
        return spriteSheet;
    }

    public void setSpriteSheet(SpriteSheet spriteSheet) {
        this.spriteSheet = spriteSheet;
    }

    @Override
    public void update() {
        actionManager.update();
    }

    @Override
    public void render(SpriteBatch sb) {
        sb.setColor(Color.WHITE);
        if (spriteSheet != null) {
            sb.draw(spriteSheet.getSprite(), getX(true), getY(true), getHeight(), getHeight());
            FontHelper.renderSmartText(sb, this.font, this.id + " - " + spriteSheet.getPath(), getX(true) + getHeight(), getY(true) + getHeight() / 2.0F + this.font.getLineHeight() / 2.0F, Color.WHITE);
        }
    }

    @Override
    public Container<? extends Element> getParent() {
        return parent;
    }

    @Override
    public void setParent(Container<? extends Element> parent) {
        this.parent = parent;
    }

    @Override
    public float getX(boolean isAbsolute) {
        return getParent() != null ? getParent().getX(isAbsolute) : 0;
    }

    @Override
    public float getY(boolean isAbsolute) {
        return getParent() != null ? getParent().getY(isAbsolute) : 0;
    }

    @Override
    public void setX(float x) {

    }

    @Override
    public void setY(float y) {

    }

    @Override
    public float getWidth() {
        return getParent() != null ? getParent().getWidth() : Gdx.graphics.getWidth();
    }

    @Override
    public float getHeight() {
        return getParent() != null ? getParent().getHeight() : Gdx.graphics.getHeight();
    }

    @Override
    public void setWidth(float width) {

    }

    @Override
    public void setHeight(float height) {

    }

    @Override
    public Color getBackground() {
        return null;
    }

    @Override
    public void setBackground(Color background) {

    }

    @Override
    public boolean isResizable() {
        return false;
    }

    @Override
    public void setResizable(boolean resizable) {

    }

    @Override
    public boolean enabled() {
        return true;
    }

    @Override
    public void setEnabled(boolean enabled) {

    }

    @Override
    public boolean visible() {
        return true;
    }

    @Override
    public void setVisible(boolean visible) {

    }

    @Override
    public void dispose() {

    }

    @Override
    public boolean clickDown(int screenX, int screenY, int button) {
        mouseDown = screenX > getX(true) && screenX < getX(true) + getWidth() && Gdx.graphics.getHeight() - screenY > getY(true)
                && Gdx.graphics.getHeight() - screenY < getY(true) + getHeight();
        if (mouseDown && button == Input.Buttons.RIGHT) {
            int index = ((SpriteSheetList)getParent().getParent()).indexOf(this);
            GMenuList menuList = new GMenuList();
            menuList.setWidth(250.0F);
            menuList.setX(screenX);

            List<String> strings = LocalizedStrings.getStrings("SpriteSheetItem");
            List<String> fileSelectWindowStrings = LocalizedStrings.getStrings("FileSelectWindow");
            GMenuItem newSprite = new GMenuItem(menuList.getWidth(), menuList.getMenuItemHeight(), strings.get(0) + "...", FontHelper.getFont(FontKeys.SIM_HEI_14)) {
                @Override
                public void onClick() {
                    GFileSelectWindow fileSelectWindow = new GFileSelectWindow(FontHelper.getFont(FontKeys.SIM_HEI_14));
                    fileSelectWindow.setTitle(strings.get(1));
                    fileSelectWindow.setConfirmText(fileSelectWindowStrings.get(0));
                    fileSelectWindow.setCancelText(fileSelectWindowStrings.get(1));
                    fileSelectWindow.setBackText(fileSelectWindowStrings.get(2));
                    fileSelectWindow.setFileFilter(file -> file.isDirectory() || file.getName().endsWith(".png") || file.getName().endsWith(".PNG"));
                    fileSelectWindow.setPath(Project.currentProject.getFilePath());
                    fileSelectWindow.setConfirmCallback((path, selectedFiles) -> {
                        if (!selectedFiles.isEmpty()) {
                            String filepath = selectedFiles.get(0).getAbsolutePath();
                            Texture region = new Texture(Gdx.files.absolute(filepath));
                            SpriteSheet spriteSheet = new SpriteSheet();
                            spriteSheet.setSprite(region);
                            spriteSheet.setPath(filepath.replace(Project.currentProject.getFilePath() + File.separator, ""));
                            spriteSheet.loadAtlasData();
                            int id = IdGenerator.nextSpriteSheetId();
                            Project.currentProject.addSpriteSheet(id, spriteSheet);
                            SpriteSheetItem item = new SpriteSheetItem(FontHelper.getFont(FontKeys.SIM_HEI_14));
                            item.setSpriteSheet(spriteSheet);
                            item.setId(Integer.toString(id));
                            regionManager.getSpriteSheetList().addItem(index, item);
                            if (regionManager.getSpriteSheetList().getElements().size() * regionManager.getSpriteSheetList().getCellHeight() > regionManager.getScrollPane().getPlainHeight()) {
                                regionManager.getScrollPane().setPlainHeight(regionManager.getScrollPane().getPlainHeight() + regionManager.getSpriteSheetList().getCellHeight());
                            }
                            Operation operation = new Operation() {
                                @Override
                                public void undo() {
                                    IdGenerator.setSpriteSheetId(id);
                                    Project.currentProject.removeSpriteSheet(id);
                                    regionManager.getSpriteSheetList().removeItem(item);
                                    actionManager.addToSequenceBot(new AbstractAction.Builder().setActionBody(duration -> {
                                        if (Project.currentProject.getSpriteSheets().size() * regionManager.getSpriteSheetList().getCellHeight() < regionManager.getScrollPane().getPlainHeight()
                                                && regionManager.getScrollPane().getPlainHeight() - regionManager.getSpriteSheetList().getCellHeight() > regionManager.getScrollPane().getHeight()) {
                                            regionManager.getScrollPane().setPlainHeight(regionManager.getScrollPane().getPlainHeight() - regionManager.getSpriteSheetList().getCellHeight());
                                        }
                                        return true;
                                    }).build());
                                    spriteSheet.dispose();
                                }

                                @Override
                                public void redo() {
                                    IdGenerator.setSpriteSheetId(id + 1);
                                    Texture region = new Texture(Gdx.files.absolute(filepath));
                                    spriteSheet.setSprite(region);
                                    Project.currentProject.addSpriteSheet(id, spriteSheet);
                                    regionManager.getSpriteSheetList().addItem(item);
                                    if (regionManager.getSpriteSheetList().getElements().size() * regionManager.getSpriteSheetList().getCellHeight() > regionManager.getScrollPane().getPlainHeight()) {
                                        regionManager.getScrollPane().setPlainHeight(regionManager.getScrollPane().getPlainHeight() + regionManager.getSpriteSheetList().getCellHeight());
                                    }
                                }
                            };
                            operation.setName(strings.get(2));
                            ((HistoryPanel) FormManager.getInstance().getContainerById(HistoryPanel.ID)).addOperation(operation);
                        }
                    });
                }
            };
            if (Project.currentProject.getFilePath() == null) {
                newSprite.setEnabled(false);
            }
            GMenuItem deleteSprite = new GMenuItem(menuList.getWidth(), menuList.getMenuItemHeight(), strings.get(3), FontHelper.getFont(FontKeys.SIM_HEI_14)) {
                @Override
                public void onClick() {
                    Project.currentProject.removeSpriteSheet(Integer.parseInt(id));
                    ((SpriteSheetList)SpriteSheetItem.this.getParent().getParent()).removeItem(SpriteSheetItem.this);
                    spriteSheet.getSprite().dispose();
                    actionManager.addToSequenceBot(new AbstractAction.Builder().setActionBody(duration -> {
                        if (Project.currentProject.getSpriteSheets().size() * regionManager.getSpriteSheetList().getCellHeight() < regionManager.getScrollPane().getPlainHeight()
                            && regionManager.getScrollPane().getPlainHeight() - regionManager.getSpriteSheetList().getCellHeight() > regionManager.getScrollPane().getHeight()) {
                            regionManager.getScrollPane().setPlainHeight(regionManager.getScrollPane().getPlainHeight() - regionManager.getSpriteSheetList().getCellHeight());
                        }
                        return true;
                    }).build());
                    Operation operation = new Operation() {
                        @Override
                        public void undo() {
                            Texture region;
                            try {
                                region = new Texture(Gdx.files.absolute(spriteSheet.getPath()));
                            } catch (Exception e) {
                                try {
                                    region = new Texture(Gdx.files.absolute(Project.currentProject.getFilePath() + File.separator + spriteSheet.getPath()));
                                } catch (Exception e1) {
                                    Pixmap pixmap = new Pixmap(20, 20, Pixmap.Format.RGBA8888);
                                    pixmap.setColor(Color.RED.cpy());
                                    pixmap.fill();
                                    region = new Texture(pixmap);
                                }
                            }
                            spriteSheet.setSprite(region);
                            Project.currentProject.addSpriteSheet(Integer.parseInt(id), spriteSheet);
                            regionManager.getSpriteSheetList().addItem(SpriteSheetItem.this);
                            if (regionManager.getSpriteSheetList().getElements().size() * regionManager.getSpriteSheetList().getCellHeight() > regionManager.getScrollPane().getPlainHeight()) {
                                regionManager.getScrollPane().setPlainHeight(regionManager.getScrollPane().getPlainHeight() + regionManager.getSpriteSheetList().getCellHeight());
                            }
                        }

                        @Override
                        public void redo() {
                            Project.currentProject.removeSpriteSheet(Integer.parseInt(id));
                            ((SpriteSheetList)SpriteSheetItem.this.getParent().getParent()).removeItem(SpriteSheetItem.this);
                            spriteSheet.getSprite().dispose();
                            actionManager.addToSequenceBot(new AbstractAction.Builder().setActionBody(duration -> {
                                if (Project.currentProject.getSpriteSheets().size() * regionManager.getSpriteSheetList().getCellHeight() < regionManager.getScrollPane().getPlainHeight()
                                        && regionManager.getScrollPane().getPlainHeight() - regionManager.getSpriteSheetList().getCellHeight() > regionManager.getScrollPane().getHeight()) {
                                    regionManager.getScrollPane().setPlainHeight(regionManager.getScrollPane().getPlainHeight() - regionManager.getSpriteSheetList().getCellHeight());
                                }
                                return true;
                            }).build());
                        }
                    };
                    operation.setName(strings.get(4));
                    ((HistoryPanel) FormManager.getInstance().getContainerById(HistoryPanel.ID)).addOperation(operation);
                }
            };
            GMenuItem reloadSprite = new GMenuItem(menuList.getWidth(), menuList.getMenuItemHeight(), strings.get(5), FontHelper.getFont(FontKeys.SIM_HEI_14)) {
                @Override
                public void onClick() {
                    Texture region;
                    try {
                        region = new Texture(Gdx.files.absolute(spriteSheet.getPath()));
                    } catch (Exception e) {
                        try {
                            region = new Texture(Gdx.files.absolute(Project.currentProject.getFilePath() + File.separator + spriteSheet.getPath()));
                        } catch (Exception e1) {
                            Pixmap pixmap = new Pixmap(20, 20, Pixmap.Format.RGBA8888);
                            pixmap.setColor(Color.RED.cpy());
                            pixmap.fill();
                            region = new Texture(pixmap);
                        }
                    }
                    spriteSheet.getSprite().dispose();
                    spriteSheet.setSprite(region);
                    spriteSheet.loadAtlasData();
                }
            };
            GMenuItem replaceSprite = new GMenuItem(menuList.getWidth(), menuList.getMenuItemHeight(), strings.get(6) + "...", FontHelper.getFont(FontKeys.SIM_HEI_14)) {
                @Override
                public void onClick() {
                    GFileSelectWindow fileSelectWindow = new GFileSelectWindow(FontHelper.getFont(FontKeys.SIM_HEI_14));
                    fileSelectWindow.setConfirmText(fileSelectWindowStrings.get(0));
                    fileSelectWindow.setCancelText(fileSelectWindowStrings.get(1));
                    fileSelectWindow.setBackText(fileSelectWindowStrings.get(2));
                    fileSelectWindow.setTitle(strings.get(7));
                    fileSelectWindow.setFileFilter(file -> file.isDirectory() || file.getName().endsWith(".png") || file.getName().endsWith(".PNG"));
                    fileSelectWindow.setPath(Project.currentProject.getFilePath());
                    fileSelectWindow.setConfirmCallback((path, selectedFiles) -> {
                        String pathBefore = spriteSheet.getPath();
                        if (!selectedFiles.isEmpty()) {
                            String filepath = selectedFiles.get(0).getAbsolutePath();
                            Texture region = new Texture(Gdx.files.absolute(filepath));
                            spriteSheet.dispose();
                            spriteSheet.setSprite(region);
                            spriteSheet.setPath(filepath.replace(Project.currentProject.getFilePath() + File.separator, ""));
                            spriteSheet.loadAtlasData();
                            Operation operation = new Operation() {
                                @Override
                                public void undo() {
                                    Texture region = new Texture(Gdx.files.absolute(Project.currentProject.getFilePath() + File.separator + pathBefore));
                                    spriteSheet.dispose();
                                    spriteSheet.setSprite(region);
                                    spriteSheet.setPath(pathBefore);
                                    spriteSheet.loadAtlasData();
                                }

                                @Override
                                public void redo() {
                                    Texture region = new Texture(Gdx.files.absolute(filepath));
                                    spriteSheet.dispose();
                                    spriteSheet.setSprite(region);
                                    spriteSheet.setPath(filepath.replace(Project.currentProject.getFilePath() + File.separator, ""));
                                    spriteSheet.loadAtlasData();
                                }
                            };
                            operation.setName(strings.get(8));
                            ((HistoryPanel) FormManager.getInstance().getContainerById(HistoryPanel.ID)).addOperation(operation);
                        }
                    });
                }
            };
            GMenuItem createTrackWithThisSprite = new GMenuItem(menuList.getWidth(), menuList.getMenuItemHeight(), strings.get(9) + "...", FontHelper.getFont(FontKeys.SIM_HEI_14)) {
                @Override
                public void onClick() {
                    TrackPane trackPane = (TrackPane) FormManager.getInstance().getContainerById(TrackPane.ID);
                    if (spriteSheet.hasAtlasData()) {
                        QuestionBox questionBox = new QuestionBox(strings.get(9), strings.get(10), FontHelper.getFont(FontKeys.SIM_HEI_14), LocalizedStrings.getStrings("SpriteSheetItem.ImportSerialQuestion"));
                        questionBox.setConfirmCallback(() -> trackPane.newTrack(Integer.parseInt(id), track -> new TextInputWindow(strings.get(12), "0,0", FontHelper.getFont(FontKeys.SIM_HEI_14)).setConfirmCallback(pos -> {
                            float xOrigin = 0.0F;
                            float yOrigin = 0.0F;
                            boolean isOriginDataValid = false;
                            try {
                                if (pos.indexOf(",") > 0) {
                                    String[] tuple = pos.split(",");
                                    xOrigin = Float.parseFloat(tuple[0].trim());
                                    yOrigin = Float.parseFloat(tuple[1].trim());
                                    isOriginDataValid = true;
                                }
                            } catch (Exception ignored) {
                            }
                            List<KeyFrame> keyFrames = new ArrayList<>();
                            for (Map.Entry<String, TextureAtlas.TextureAtlasData.Region> e : spriteSheet.getRegions().entrySet()) {
                                TextureAtlas.TextureAtlasData.Region region = e.getValue();
                                KeyFrame keyFrame = new KeyFrame();
                                keyFrame.xCrop = region.left;
                                keyFrame.yCrop = region.top;
                                keyFrame.width = region.width;
                                keyFrame.height = region.height;
                                if (isOriginDataValid) {
                                    keyFrame.xPivot = xOrigin - region.offsetX;
                                    keyFrame.yPivot = region.height + region.offsetY - yOrigin;
                                } else {
                                    keyFrame.xPivot = region.width / 2.0F;
                                    keyFrame.yPivot = region.height / 2.0F;
                                }
                                if (region.rotate) {
                                    int tmp = keyFrame.width;
                                    keyFrame.width = keyFrame.height;
                                    keyFrame.height = tmp;
                                    keyFrame.rotation = 90;
                                    float tmpF = keyFrame.xPivot;
                                    keyFrame.xPivot = keyFrame.yPivot;
                                    keyFrame.yPivot = tmpF;
                                    keyFrame.yPivot += (region.originalWidth - region.width) / 2.0F - region.offsetX;
                                }
                                keyFrame.delay = 1;
                                keyFrame.xScale = 100.0F;
                                keyFrame.yScale = 100.0F;
                                keyFrame.tint = Color.WHITE.cpy();
                                keyFrame.colorOffset = Color.WHITE.cpy();
                                keyFrames.add(keyFrame);
                                track.getContent(Project.currentProject.getCurrentAnimation().getName()).getKeyFrames().add(keyFrame);
                            }
                            Operation operation = new Operation() {
                                @Override
                                public void undo() {
                                    track.getContent(Project.currentProject.getCurrentAnimation().getName()).getKeyFrames().removeAll(keyFrames);
                                }

                                @Override
                                public void redo() {
                                    track.getContent(Project.currentProject.getCurrentAnimation().getName()).getKeyFrames().addAll(keyFrames);
                                }
                            };
                            operation.setName(strings.get(11));
                            ((HistoryPanel) FormManager.getInstance().getContainerById(HistoryPanel.ID)).addOperation(operation);
                        })));
                        questionBox.setCancelCallback(() -> trackPane.newTrack(Integer.parseInt(id), null));
                    } else {
                        trackPane.newTrack(Integer.parseInt(id), null);
                    }
                }
            };
            createTrackWithThisSprite.setSplitLine(false);
            menuList.addElement(newSprite);
            menuList.addElement(deleteSprite);
            menuList.addElement(reloadSprite);
            menuList.addElement(replaceSprite);
            menuList.addElement(createTrackWithThisSprite);
            menuList.setY(Gdx.graphics.getHeight() - screenY - menuList.getHeight());
            FormManager.getInstance().addContainer(menuList);
            FormManager.getInstance().moveToTop(menuList);
        }
        return mouseDown;
    }

    @Override
    public boolean clickUp(int screenX, int screenY, int button) {
        if (mouseDown) {
            mouseDown = false;
            return true;
        }
        return false;
    }

    @Override
    public boolean mouseDragged(int screenX, int screenY) {
        return false;
    }

    @Override
    public boolean mouseMoved(int screenX, int screenY) {
        return false;
    }

    @Override
    public boolean scrolled(int amount) {
        return false;
    }

    @Override
    public boolean moveToElementBorder(Element element) {
        return false;
    }
}
